 // * LICENCE
 // * You are free to use this for any application.
 // * Consequences of this assembly on any machine is not guaranteed nor 
 // * is the author responsible for is working/side-effects.
 // * You are not allowed to reverse-engineer this.
 // * Any suggestions/bugs can be reported to suniljagadish@gmail.com
 // * Author:
 // * SUNIL JAGADISH
 // * http://suniljagadish.wordpress.com

using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Collections.Generic;
using System.Text;

namespace CAPTCHA
{
    public class CaptchaGenerator
    {
        private int _ImageWidth = 200;
        private int _ImageHeight = 50;
        private int _CaptchaLength = 6;
        private string _CaptchaText = "";

        private Color _CaptchaColor;
        /// <summary>
        /// Custom single colour to generate Captcha.
        /// </summary>
        public Color CaptchaColor
        {
            set
            {
                _CaptchaColor = value;
            }
        }

        private string[] charset = {  "I","6","a","R","8","W","b","c","B","9","d","X"
                                    ,"e","L","f","E","5","g","H","8","Y","h","V"
                                    ,"i","j","C","M","k","T","l","D","m","U","2"
                                    ,"Q","n","J","o","3","A","7","p","q","Z","r"
                                    ,"s","F","t","S","u","4","P","v","K","w","G"
                                    ,"x","O","y","z","0","N","1","f","E","5","g"
                                    ,"1","J","o","3","A","9"};
        private string[] fontset = {  "Georgia","Verdana","Tahoma"
                                    ,"Arial","Times New Roman","Garamond","Agency FB" };


        private Bitmap BMP;
        private Graphics G;

        /// <summary>
        /// Uses default ImageWidth(200), ImageHeight(50) and CaptchaLength(6).
        /// </summary>
        public CaptchaGenerator()
        {
            BMP = new Bitmap(_ImageWidth, _ImageHeight);
            G = Graphics.FromImage(BMP);
        }

        /// <summary>
        /// Set custom ImageWidth, ImageHeight and CaptchaLength.
        /// </summary>
        /// <param name="ImageWidth">Width of the GIF image produced</param>
        /// <param name="Imageheight">Height of the GIF image produced</param>
        /// <param name="CaptchaLength">Number of characters in the Captcha</param>
        /// <param name="ImagePath"></param>
        public CaptchaGenerator(int ImageWidth, int Imageheight, int CaptchaLength, string ImagePath)
        {
            //TODO: Check if CaptchaLength > ImageWidth
            _ImageWidth = ImageWidth;
            _ImageHeight = Imageheight;
            _CaptchaLength = CaptchaLength;

            BMP = new Bitmap(_ImageWidth, _ImageHeight);
            G = Graphics.FromImage(BMP);
        }

        /// <summary>
        /// Generates random coloured Captcha.
        /// </summary>
        public void ResetCaptchaColor()
        {
            _CaptchaColor = Color.Empty;
        }

        private string GetRandomString()
        {
            Random rand = new Random();
            StringBuilder sb = new StringBuilder(_CaptchaLength);

            for (int i = 0; i < _CaptchaLength; i++)
            {
                sb.Append(charset[rand.Next(charset.Length - 1)]);
            }
            return sb.ToString();
        }

        private void Generate()
        {
            G.FillRectangle(new SolidBrush(Color.White), new Rectangle(0, 0, _ImageWidth, _ImageHeight));

            Random rand = new Random();
            Random Colorrand = new Random();
            Random anglerand = new Random();
            Random sizerand = new Random();

            // Generate random arcs and lines to distort image
            Random numlines = new Random();
            Random coordinates = new Random();
            Random brushsize = new Random();
            for (int i = 0; i < numlines.Next(20, 50); i++)
            {
                Pen blackPen;

                if (_CaptchaColor != Color.Empty)
                {
                    Color bgcolor = Color.FromArgb(80, _CaptchaColor);
                    blackPen = new Pen(bgcolor, (float)brushsize.Next(4));
                }
                else
                {
                    blackPen = new Pen(Color.FromArgb(Colorrand.Next(150, 200), Colorrand.Next(120, 240), Colorrand.Next(150, 200)), (float)brushsize.Next(4));
                }

                Rectangle rect = new Rectangle(coordinates.Next(0, _ImageWidth), coordinates.Next(0, _ImageHeight), coordinates.Next(50, _ImageWidth), coordinates.Next(30, _ImageHeight));
                float startAngle = (float)coordinates.Next(20, 45);
                float sweepAngle = (float)coordinates.Next(40, 170);
                G.DrawArc(blackPen, rect, startAngle, sweepAngle);
                G.DrawLine(blackPen, new Point(coordinates.Next(0, _ImageWidth), coordinates.Next(0, _ImageHeight)), new Point(coordinates.Next(0, _ImageWidth), coordinates.Next(0, _ImageHeight)));
            }

            _CaptchaText = GetRandomString();

            // Initial margin from left and subsequent distance between characters
            int xpos = 10;

            for (int i = 0; i < _CaptchaLength; i++)
            {
                if (_CaptchaColor != Color.Empty)
                {
                    G.DrawString(_CaptchaText[i].ToString()
                        , new Font(fontset[rand.Next(fontset.Length - 1)], (float)sizerand.Next(20, 30))
                        , new SolidBrush(_CaptchaColor)
                        , new PointF((float)xpos, 0.0F));
                }
                else
                {
                    G.DrawString(_CaptchaText[i].ToString()
                        , new Font(fontset[rand.Next(fontset.Length - 1)], (float)sizerand.Next(20, 30))
                        , new SolidBrush(Color.FromArgb(Colorrand.Next(240), Colorrand.Next(100), Colorrand.Next(220)))
                        , new PointF((float)xpos, 0.0F));
                }

                xpos += sizerand.Next(20, 30);
            }
        }

        /// <summary>
        /// Generates Captcha.
        /// </summary>
        /// <param name="ImagePath">Patch where the GIF file is to be stored.</param>
        /// <returns></returns>
        public string GenerateCaptcha(string ImagePath)
        {
            Generate();
            BMP.Save(ImagePath, ImageFormat.Gif);
            return _CaptchaText;
        }
    }
}
